/*
 *  TransactionalOperation.java        22-ott-2009
 *   
 * Copyright (c) 2009 mentalsmash.org
 *  
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *  http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 * 
 *  The use of the Apache License does not indicate that this project is
 *  affiliated with the Apache Software Foundation.
 */

package org.mentalsmash.tazio.transactions.management;

import java.io.Serializable;

import org.mentalsmash.documentation.Concurrency;
import org.mentalsmash.tazio.commons.identifiers.OID;
import org.mentalsmash.tazio.transactions.objects.RootSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * The <tt>TransactionalOperation</tt> is an abstract class the is used as base
 * class for implementations that record operations on transactional objects
 * <p>
 * A transactional operation may essentially be of two types: <br />
 * <ul>
 * <li><strong>Query</strong>: read-only operation
 * <li><strong>Update</strong>: write-only operation
 * </ul>
 * An operation may both read and write to an object: in that case this
 * operation is bisect in a couple of Query and Update operations
 * </p>
 * 
 * @author Andrea Reale
 * @version 0.0.1
 * 
 */

@Concurrency(concurrentAccess = false, specification = "TransactionalOperation is not synchronized."
	+ " User of this container class must"
	+ " provide synchronization on access to those instances."

)
public abstract class TransactionalOperation implements Serializable {

    protected final static Logger log = LoggerFactory
	    .getLogger(TransactionalOperation.class);
    private static final long serialVersionUID = -5002624261710796318L;
    /**
     * the unique identifier for the object that is the target of the operation
     */
    private final OID targetObject;
    private String propertyName;

    /**
     * the version of the target object at the moment this operation started
     */
    private final long targetVersion;
    
    private final boolean writeOperation;

    public TransactionalOperation(final OID targetObject,
	    final long originalVersion,boolean isWriteOperation) {
	this.targetObject = targetObject;
	this.targetVersion = originalVersion;
	this.writeOperation = isWriteOperation;
    }
    
    public boolean isWriteOperation(){
	return this.writeOperation;
    }

    /**
     * Returns the unique identifier of the object on which the operation is
     * performed
     * 
     * @return the unique identifier of the object on which the operation is
     *         performed
     */
    public OID getTargetObject() {
	return this.targetObject;
    }

    /**
     * Gets the version that the <tt>TransactionalObject</tt> on which the write
     * operation has been performed had before the write operation itself.
     * <p>
     * e.g. if a <tt>TransactionalObject</tt>'s property is written while that
     * object's version was 3, this method will return 3
     * </p>
     * 
     * @return the version that the <tt>TransactionalObject</tt> on which the
     *         write operation has been performed had before the write operation
     *         itself.
     */
    public long getOriginalVersion() {
	return this.targetVersion;
    }

    /**
     * The name of the property that was written on the given
     * <tt>TransactionalObject/<tt>
     * 
     * @return The name of the property that was written on the given
     *         <tt>TransactionalObject/<tt>
     */
    public String getPropertyName() {
	return this.propertyName;
    }

    /**
     * Set the name of the property that got written by this Update operation
     * 
     * @param propertyName
     *            the name of the property that got written by this update
     */
    public void setPropertyName(final String propertyName) {
	this.propertyName = propertyName;
    }

    public abstract void commitOperation(RootSet rs);

    @Override
    public int hashCode() {
	final int prime = 31;
	int result = 1;
	result = prime
		* result
		+ ((this.propertyName == null) ? 0 : this.propertyName
			.hashCode());
	result = prime
		* result
		+ ((this.targetObject == null) ? 0 : this.targetObject
			.hashCode());
	result = prime * result
		+ (int) (this.targetVersion ^ (this.targetVersion >>> 32));
	return result;
    }

    @Override
    public boolean equals(final Object obj) {
	if (this == obj) {
	    return true;
	}
	if (obj == null) {
	    return false;
	}
	if (getClass() != obj.getClass()) {
	    return false;
	}
	final TransactionalOperation other = (TransactionalOperation) obj;
	if (this.propertyName == null) {
	    if (other.propertyName != null) {
		return false;
	    }
	} else if (!this.propertyName.equals(other.propertyName)) {
	    return false;
	}
	if (this.targetObject == null) {
	    if (other.targetObject != null) {
		return false;
	    }
	} else if (!this.targetObject.equals(other.targetObject)) {
	    return false;
	}
	if (this.targetVersion != other.targetVersion) {
	    return false;
	}
	return true;
    }

    @Override
    public String toString() {
	final StringBuilder builder = new StringBuilder();
	builder.append("TransactionalOperation [propertyName=");
	builder.append(this.propertyName);
	builder.append(", targetObject=");
	builder.append(this.targetObject);
	builder.append("]");
	return builder.toString();
    }

}
